#include "Primitive.h"

namespace RTRT
{

#define FINDMINMAX( x0, x1, x2, min, max ) \
  min = max = x0; if(x1<min) min=x1; if(x1>max) max=x1; if(x2<min) min=x2; if(x2>max) max=x2;
// X-tests
#define AXISTEST_X01( a, b, fa, fb )											\
	p0 = a * v0.cell[1] - b * v0.cell[2], p2 = a * v2.cell[1] - b * v2.cell[2]; \
    if (p0 < p2) { min = p0; max = p2;} else { min = p2; max = p0; }			\
	rad = fa * a_BoxHalfsize.cell[1] + fb * a_BoxHalfsize.cell[2];				\
	if (min > rad || max < -rad) return 0;
#define AXISTEST_X2( a, b, fa, fb )												\
	p0 = a * v0.cell[1] - b * v0.cell[2], p1 = a * v1.cell[1] - b * v1.cell[2];	\
    if (p0 < p1) { min = p0; max = p1; } else { min = p1; max = p0;}			\
	rad = fa * a_BoxHalfsize.cell[1] + fb * a_BoxHalfsize.cell[2];				\
	if(min>rad || max<-rad) return 0;
// Y-tests
#define AXISTEST_Y02( a, b, fa, fb )											\
	p0 = -a * v0.cell[0] + b * v0.cell[2], p2 = -a * v2.cell[0] + b * v2.cell[2]; \
    if(p0 < p2) { min = p0; max = p2; } else { min = p2; max = p0; }			\
	rad = fa * a_BoxHalfsize.cell[0] + fb * a_BoxHalfsize.cell[2];				\
	if (min > rad || max < -rad) return 0;
#define AXISTEST_Y1( a, b, fa, fb )												\
	p0 = -a * v0.cell[0] + b * v0.cell[2], p1 = -a * v1.cell[0] + b * v1.cell[2]; \
    if (p0 < p1) { min = p0; max = p1; } else { min = p1; max = p0; }			\
	rad = fa * a_BoxHalfsize.cell[0] + fb * a_BoxHalfsize.cell[2];				\
	if (min > rad || max < -rad) return 0;
// Z-tests
#define AXISTEST_Z12( a, b, fa, fb )											\
	p1 = a * v1.cell[0] - b * v1.cell[1], p2 = a * v2.cell[0] - b * v2.cell[1]; \
    if(p2 < p1) { min = p2; max = p1; } else { min = p1; max = p2; }			\
	rad = fa * a_BoxHalfsize.cell[0] + fb * a_BoxHalfsize.cell[1];				\
	if (min > rad || max < -rad) return 0;
#define AXISTEST_Z0( a, b, fa, fb )												\
	p0 = a * v0.cell[0] - b * v0.cell[1], p1 = a * v1.cell[0] - b * v1.cell[1];	\
    if(p0 < p1) { min = p0; max = p1; } else { min = p1; max = p0; }			\
	rad = fa * a_BoxHalfsize.cell[0] + fb * a_BoxHalfsize.cell[1];				\
	if (min > rad || max < -rad) return 0;
	
	

	Triangle :: Triangle(Vertex *v0,Vertex *v1,Vertex *v2,const Vector3& normal)
	{
		m_vertices[0] = v0;
		m_vertices[1] = v1;
		m_vertices[2] = v2;
		Vector3 va = m_vertices[0]->getPosition();
		Vector3 vb = m_vertices[1]->getPosition();
		Vector3 vc = m_vertices[2]->getPosition();
		Vector3 c = vb - va;
		Vector3 b = vc - va;
		
		m_material = 0;
		m_normal = normal;
		int u,v;
		if (Math::Fabs(m_normal.x) > Math::Fabs(m_normal.y))
		{
			if (Math::Fabs(m_normal.x) > Math::Fabs(m_normal.z))
				m_k = 0;
			else
				m_k = 2;
		}
		else 
		{
			if (Math::Fabs(m_normal.y) > Math::Fabs(m_normal.z))
				m_k = 1;
			else 
				m_k = 2;
		}
		u = (m_k + 1) % 3;
		v = (m_k + 2) % 3;
		
		// Precompute
		float revK = 1.0f / m_normal[m_k];
		m_nu = m_normal[u] * revK;
		m_nv = m_normal[v] * revK;
		m_nd = m_normal.dotProduct(va) * revK;
		// First line equation
		float reci = 1.0f / (b[u] * c[v] - b[v] * c[u]);
		m_bnu = b[u] * reci;
		m_bnv = -b[v] * reci;
		// Second line equation
		m_cnu = c[v] * reci;
		m_cnv = -c[u] * reci;
		// Finalize normal
		m_normal.normalise();
		m_vertices[0]->setNormal(m_normal);
		m_vertices[1]->setNormal(m_normal);
		m_vertices[2]->setNormal(m_normal);
	}
	
    /*
     * Ray - Triangle Intersection Detection
     */
    int Triangle :: intersectRay(Ray *ray, float &dist)
    {
        
    }

	bool Triangle :: planeBoxOverlap(const Vector3& normal, const Vector3& vert, Vector3& maxBox)
	{
		Vector3 vMin, vMax;
		for( int i = 0; i < 3; i++ )
		{
			float v = vert.cell(i);
			if (normal.cell(i) > 0.0f)
			{
				vMin.cell(i) = -maxBox.cell(i) - v;
				vMax.cell(i) =  maxBox.cell(i) - v;
			}
			else
			{
				vMin.cell(i) =  maxBox.cell(i) - v;
				vMax.cell(i) = -maxBox.cell(i) - v;
			}
		}
		
		/*
		if (DOT( normal, vMin) > 0.0f) 
			return false;
		if (DOT( normal, vMax) >= 0.0f) 
			return true;
		*/
			
		if (normal.dotProduct(vMin) > 0.0f)
			return false;
		if (normal.dotProduct(vMax) >= 0.0f)
			return true;
			
		return false;
	}

	bool Triangle :: intersectBox(const Vector3& boxCenter, const Vector3& boxHalfSize) 
	{
		Vector3 v0, v1, v2, normal, e0, e1, e2;
		float min, max, p0, p1, p2, rad, fex, fey, fez;
		vo = m_vertices[0].getPosition() - boxCenter;
		v1 = m_vertices[1].getPosition() - boxCenter;
		v2 = m_vertices[2].getPosition() - boxCenter;
		e0 = v1 - v0, e1 = v2 - v1, e2 = v0 - v2;
		
		fex = Math::Fabs(e0.x);
		fey = Math::Fabs(e0.y);
		fez = Math::Fabs(e0.z);
		AXISTEST_X01( e0.cell(2), e0.cell(1), fez, fey );
		AXISTEST_Y02( e0.cell(2), e0.cell(0), fez, fex );
		AXISTEST_Z12( e0.cell(1), e0.cell(0), fey, fex );
		
		fex = Math::Fabs(e1.x);
		fey = Math::Fabs(e1.y);
		fez = Math::Fabs(e1.z);
		AXISTEST_X01( e1.cell(2), e1.cell(1), fez, fey );
		AXISTEST_Y02( e1.cell(2), e1.cell(0), fez, fex );
		AXISTEST_Z0 ( e1.cell(1), e1.cell(0), fey, fex );
		
		fex = Math::Fabs( e2.cell(0) );
		fey = Math::Fabs( e2.cell(1) );
		fez = Math::Fabs( e2.cell(2) );
		AXISTEST_X2 ( e2.cell(2), e2.cell(1), fez, fey );
		AXISTEST_Y1 ( e2.cell(2), e2.cell(0), fez, fex );
		AXISTEST_Z12( e2.cell(1), e2.cell(0), fey, fex );
		
		FINDMINMAX( v0.cell(0), v1.cell(0), v2.cell(0), min, max );
		if (min > boxHalfSize.cell(0) || max < -boxHalfSize.cell(0)) 
			return false;
		FINDMINMAX( v0.cell(1), v1.cell(1), v2.cell(1), min, max );
		if (min > boxHalfSize.cell(1) || max < -boxHalfSize.cell(1)) 
			return false;
		FINDMINMAX( v0.cell(2), v1.cell(2), v2.cell(2), min, max );
		if (min > boxHalfSize.cell(2) || max < -boxHalfSize.cell(2)) 
			return false;
		normal = e0.crossProduct( e1 );
		if (!planeBoxOverlap( normal, v0, boxHalfSize )) 
			return false;
			
		return true;
	}

	void Triangle :: calculateRange(float& minPos, float& maxPos, int axis) 
	{
		Vector3 pos = m_vertices[0]->getPosition();
		maxPos = pos[axis];
		minPos = pos[axis];
		
		for (int i=0;i<3;i++)
		{
			Vector3 tPos = m_vertices[i]->getPosition();
			if (tPos[axis] < minPos)  
				minPos = tPos[axis];
			if (tPos[axis] > maxPos)
				maxPos = tPos[axis];
		}
	}
	
	Vector3 Triangle :: getNormal()
	{
		Vector3 n1 = m_vertices[0]->getNormal();
		Vector3 n2 = m_vertices[1]->getNormal();
		Vector3 n3 = m_vertices[2]->getNormal();
		Vector3 normal = n1 + m_u * (n2 - n1) + m_v * (n3 - n1);
		normal.normalise();
		return normal;
	}
	
	
	Color Triangle :: getColor(Vector3& color)
	{
		Color retColor;
		// if (!m_material)
		retColor = m_material->getColor();
		
		return retColor;
	}
}
